﻿namespace RuoTeng.Win32
{
    using System;
    using System.Runtime.InteropServices;

    class Mouse :RuoTeng.Win32.Message
    {
        public enum EventMode
        {
           /// <summary>通过软件模拟</summary>
            SoftwareMode = 0x001,
           /// <summary>通过硬件模拟</summary>
            HardwareMode = 0x002
        }
    /// <summary>【Win32 API】鼠标移动消息</summary> 
        public const uint WM_MOUSEMOVE = 0x0200;
    /// <summary>【Win32 API】鼠标左键按下消息</summary>
        public const uint WM_LBUTTONDOWN = 0x0201;
    ///<summary>【Win32 API】鼠标左键弹起消息</summary>
        public const uint WM_LBUTTONUP = 0x0202;
    /// <summary>【Win32 API】鼠标左键双击消息</summary>
        public const uint WM_LBUTTONDBLCLK = 0x0203;
    /// <summary>【Win32 API】鼠标右键按下消息</summary>
        public const uint WM_RBUTTONDOWN = 0x0204;
    /// <summary>【Win32 API】鼠标右键弹起消息</summary>
        public const uint WM_RBUTTONUP = 0x0205;
    /// <summary>【Win32 API】鼠标右键双击消息</summary>
        public const uint WM_RBUTTONDBLCLK = 0x0206;
    /// <summary>【Win32 API】鼠标中键按下消息</summary>
        public const uint WM_MBUTTONDOWN = 0x0207;
    /// <summary>【Win32 API】鼠标中键弹起消息</summary>
        public const uint WM_MBUTTONUP = 0x0208;
    /// <summary>【Win32 API】鼠标中键双击消息</summary>
        public const uint WM_MBUTTONDBLCLK = 0x0209;   

    /*
     * 鼠标软件模拟消息的附加事件消息
     */
    /// <summary>【Win32 API】鼠标左键按下消息</summary>
        public const int MK_LBUTTON = 0x0001;
    /// <summary>【Win32 API】鼠标右键按下消息</summary>
        public const int MK_RBUTTON = 0x0002;   //The right mouse button is down.
    /// <summary>【Win32 API】键盘shift按下消息</summary>
        public const int MK_SHIFT = 0x0004;
    /// <summary>【Win32 API】键盘ctrl按下消息</summary>
        public const int MK_CONTROL = 0x0008;
    /// <summary>【Win32 API】鼠标中键按下消息</summary>
        public const int MK_MBUTTON = 0x0010;
    /// <summary>【Win32 API】The first X button is down</summary> 
        public const int MK_XBUTTON1 = 0x0020;
    /// <summary>【Win32 API】The second X button is down</summary> 
        public const int MK_XBUTTON2 = 0x0040;
       
    /*
     * 用于硬件模拟的鼠标事件消息
     */
    /// <summary>【Win32 API】鼠标移动事件</summary> 
        public const int MOUSEEVENTF_MOVE = 0x0001;
    /// <summary>【Win32 API】鼠标左键按下事件</summary> 
        public const int MOUSEEVENTF_LEFTDOWN = 0x0002;
    /// <summary>【Win32 API】鼠标左键弹起事件</summary>
        public const int MOUSEEVENTF_LEFTUP = 0x0004;
    /// <summary>【Win32 API】鼠标右键按下事件</summary>
        public const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
    /// <summary>【Win32 API】鼠标右键弹起事件</summary>
        public const int MOUSEEVENTF_RIGHTUP = 0x0010;
    /// <summary>【Win32 API】鼠标中键按下事件</summary>
        public const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
    /// <summary>【Win32 API】鼠标中键抬起事件</summary>
        public const int MOUSEEVENTF_MIDDLEUP = 0x0040;
    /// <summary>【Win32 API】鼠标X键按下事件</summary>
        public const int MOUSEEVENTF_XDOWN = 0x0080;
    /// <summary>【Win32 API】鼠标X键弹起事件</summary>
        public const int MOUSEEVENTF_XUP = 0x0100;
    /// <summary>【Win32 API】鼠标轮滑滚动事件</summary>
        public const int MOUSEEVENTF_WHEEL = 0x0800;
    /// <summary>【Win32 API】整个虚拟桌面地图</summary>
        public const int MOUSEEVENTF_VIRTUALDESK = 0x4000;
    /// <summary>【Win32 API】绝对位置移动</summary>
        public const int MOUSEEVENTF_ABSOLUTE = 0x8000;
    /// <summary>整个屏幕的像素比例,用户鼠标移动位置坐标转换</summary>
        public const int MAXPIXEL=65535;
   
    /// <summary>
    ///【Win32 API】 该函数综合鼠标击键和鼠标动作
    /// </summary>
    /// <param name="dwFlags">
    ///        标志位集，指定点击按钮和鼠标动作的多种情况。此参数里的各位可以是下列值的任何合理组合
    ///        MOUSEEVENTF_ABSOLUTE：表明参数dX，dy含有规范化的绝对坐标。如果不设置此位，参数含有相对数据：相对于上次位置的改动位置。此标志可被设置，也可不设置，不管鼠标的类型或与系统相连的类似于鼠标的设备的类型如何。要得到关于相对鼠标动作的信息，参见下面备注部分。 
    ///        MOUSEEVENTF_MOVE：表明发生移动。 　
    ///        MOUSEEVENTF_LEFTDOWN：表明接按下鼠标左键。 　
    ///        MOUSEEVENTF_LEFTUP：表明松开鼠标左键。 　
    ///        MOUSEEVENTF_RIGHTDOWN：表明按下鼠标右键。
    ///        MOUSEEVENTF_RIGHTUP：表明松开鼠标右键。 
    ///        MOUSEEVENTF_MIDDLEDOWN：表明按下鼠标中键。
    ///        MOUSEEVENTF_MIDDLEUP：表明松开鼠标中键。 
    ///        MOUSEEVENTF_WHEEL：在Windows NT中如果鼠标有一个轮，表明鼠标轮被移动。移动的数量由dwData给出
    /// </param>
    /// <param name="dx">
    ///        指定鼠标沿x轴的绝对位置或者从上次鼠标事件产生以来移动的数量，依赖于MOUSEEVENTF_ABSOLUTE的设置。
    ///        给出的绝对数据作为鼠标的实际X坐标；给出的相对数据作为移动的mickeys数。
    ///        一个mickey表示鼠标移动的数量，表明鼠标已经移动 
    /// </param>
    /// <param name="dy">
    ///        指定鼠标沿y轴的绝对位置或者从上次鼠标事件产生以来移动的数量，依赖于MOUSEEVENTF_ABSOLUTE的设置。
    ///        给出的绝对数据作为鼠标的实际y坐标，给出的相对数据作为移动的mickeys数
    /// </param>
    /// <param name="dwData">
    ///        如果dwFlags为MOUSEEVENTF_WHEEL，则dwData指定鼠标轮移动的数量。正值表明鼠标轮向前转动，即远离用户的方向；
    ///        负值表明鼠标轮向后转动，即朝向用户。一个轮击定义为WHEEL_DELTA，即120
    /// </param>
    /// <param name="ptr">指定与鼠标事件相关的附加32位值。应用程序调用函数GetMessgeExtraInfo来获得此附加信息</param>
    /// <returns>无</returns>
        [DllImport("user32.dll", EntryPoint = "mouse_event")]
        public static extern void MouseEvent(int dwFlags, int dx, int dy, int dwData, ulong dwExtraInfo);

   /// <summary>
   /// 【Win32 API】  该函数把光标移到屏幕的指定位置。如果新位置不在由 ClipCursor函数设置的屏幕矩形区域之内，则系统自动调整坐标，使得光标在矩形之内
   /// </summary>
   /// <param name="x">指定光标的新的X坐标，以屏幕坐标表示</param>
   /// <param name="y">指定光标的新的Y坐标，以屏幕坐标表示</param>
   /// <returns>如果成功，返回非零值；如果失败，返回值是零</returns>
        [DllImport("user32.dll", EntryPoint = "SetCursorPos")]
        public static extern bool SetCursorPos(int X, int Y);
 
   /// <summary>
   /// 【Win32 API】  该函数检取光标的位置，以屏幕坐标表示
   /// </summary>
   /// <param name="lpPoint">POINT结构指针，该结构接收光标的屏幕坐标</param>
   /// <returns>如果成功，返回值非零；如果失败，返回值为零</returns>
        [DllImport("user32.dll", EntryPoint = "GetCursorPos")]
        public static extern bool GetCursorPos(out System.Drawing.Point lpPoint);

  /// <summary>
  ///软件  模拟鼠标左键单击事件
  /// </summary>
  /// <param name="hWnd">窗口句柄</param>
  /// <param name="X">X坐标</param>
  /// <param name="Y">Y坐标</param>
  /// <returns></returns>
        public static void LeftClick(IntPtr hWnd, int X, int Y)
        {
            LeftClick(hWnd, X,Y, EventMode.SoftwareMode);
        }

  /// <summary>
  ///  将坐标点 X Y转换成 32位整型 Y是高4位 X是低4位
  /// </summary>
  /// <param name="X">X坐标</param>
  /// <param name="Y">Y坐标</param>
  /// <returns></returns>
        public static int ConvertIParam(int X,int Y)
        {
            return ((X & 0xFFFF)+(Y & 0xFFFF)* 0x10000);
        }
 
  /// <summary>
  ///  将32位高低位坐标 转成坐标对象
  /// </summary>
  /// <param name="IParam">32位坐标</param>
  /// <returns></returns>
        public static System.Drawing.Point ConvertPoint(int IParam)
        {
            System.Drawing.Point point = new System.Drawing.Point();
            point.X = IParam & 0x0000FFFF;
            point.Y = (IParam >> 16) & 0xFFFF;
            return point; 
        }

  /// <summary>
  ///  根据坐标 转换为系统定义高65535 宽65535 像素大小
  /// </summary>
  /// <param name="X"></param>
  /// <param name="Y"></param>
  /// <returns></returns>
       public static System.Drawing.Point PointToPixel(int X, int Y)
       {
            int __X = (int)(((float)65535 / Sys.GetSystemMetrics(Sys.SystemMetrics.SM_CXSCREEN)) * X);
            int __Y = (int)(((float)65535 / Sys.GetSystemMetrics(Sys.SystemMetrics.SM_CYSCREEN)) * Y);
            return new System.Drawing.Point(__X, __Y);
       }

   /// <summary>
   ///  模拟鼠标左键单击事件
   /// </summary>
   /// <param name="hWnd">窗口句柄</param>
   /// <param name="X">X坐标</param>
   /// <param name="Y">Y坐标</param>
   /// <param name="mode">事件模式方式</param>
   /// <returns></returns>
        public static void LeftClick(IntPtr hWnd, int X, int Y, EventMode mode)
        {
            switch (mode)
            {  
              //通过软件方式进行鼠标模拟
              case EventMode.SoftwareMode:
              {
                  SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, ConvertIParam(X, Y));
                  SendMessage(hWnd, WM_LBUTTONUP, MK_LBUTTON, ConvertIParam(X, Y));                    
                  break;
              }
              //硬件模拟之后需要还原鼠标的坐标点           
              case EventMode.HardwareMode:
              {
                  System.Drawing.Point __sourcePoint;
                  GetCursorPos(out __sourcePoint);    //获取当前鼠标位置,并保存
                  System.Drawing.Rectangle __rectangle;
                
                  if (Window.GetWindowRect(hWnd,out __rectangle))
                  {
                     //移动鼠标坐标点
                     System.Drawing.Point __destPoint = PointToPixel(__rectangle.Left + X, __rectangle.Top + Y);
                     MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,     __destPoint.X, __destPoint.Y, 0, 0);
                     MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN, __destPoint.X, __destPoint.Y, 0, 0);
                     MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP,   __destPoint.X, __destPoint.Y, 0, 0);
                     //还原鼠标坐标点
                     __sourcePoint = PointToPixel(__sourcePoint.X, __sourcePoint.Y);
                     MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,     __sourcePoint.X, __sourcePoint.Y, 0, 0);
                  }
                  break;
              } 
            }
        }

   /// <summary>
   /// 软件 模拟鼠标右键单击事件
   /// </summary>
   /// <param name="hWnd">窗口句柄</param>
   /// <param name="X">X坐标</param>
   /// <param name="Y">Y坐标</param>
   /// <returns></returns>
        public static void RigthClick(IntPtr hWnd, int X, int Y)
        {
            RigthClick(hWnd, X, Y, EventMode.SoftwareMode);
        }

    /// <summary>
    ///  模拟鼠标右键单击事件
    /// </summary>
    /// <param name="hWnd">窗口句柄</param>
    /// <param name="X">X坐标</param>
    /// <param name="Y">Y坐标</param>
    /// <param name="mode">事件模式方式</param>
    /// <returns></returns>
        public static void RigthClick(IntPtr hWnd, int X, int Y, EventMode mode)
        {
            switch (mode)
            {
                //通过软件方式进行鼠标模拟
                case EventMode.SoftwareMode:
                {
                    SendMessage(hWnd, WM_RBUTTONDOWN, MK_RBUTTON, ConvertIParam(X, Y));
                    SendMessage(hWnd, WM_RBUTTONUP, MK_RBUTTON, ConvertIParam(X, Y));
                      break;
                }
                //硬件模拟之后需要还原鼠标的坐标点           
                case EventMode.HardwareMode:
                {
                    System.Drawing.Point __sourcePoint;
                    GetCursorPos(out __sourcePoint);    //获取当前鼠标位置,并保存
                    System.Drawing.Rectangle __rectangle;
                    if (Window.GetWindowRect(hWnd, out __rectangle))
                    {
                      //移动鼠标坐标点
                      System.Drawing.Point __destPoint = PointToPixel(__rectangle.Left + X, __rectangle.Top + Y);
                      MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,      __destPoint.X, __destPoint.Y, 0, 0);
                      MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN, __destPoint.X, __destPoint.Y, 0, 0);
                      MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP,   __destPoint.X, __destPoint.Y, 0, 0);
                     //还原鼠标坐标点
                      __sourcePoint = PointToPixel(__sourcePoint.X, __sourcePoint.Y);
                      MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,     __sourcePoint.X,__sourcePoint.Y, 0, 0);
                     }
                     break;
                }
            }
        }


     /// <summary>
     ///软件  模拟鼠标左键双击事件
     /// </summary>
     /// <param name="hWnd">窗口句柄</param>
     /// <param name="X">X坐标</param>
     /// <param name="Y">Y坐标</param>
     /// <returns></returns>
        public static void LeftDoubleClick(IntPtr hWnd, int X, int Y)
        {
            LeftDoubleClick(hWnd, X, Y, EventMode.SoftwareMode);
        }

      /// <summary>
      ///  模拟鼠标左键双击事件
      /// </summary>
      /// <param name="hWnd">窗口句柄</param>
      /// <param name="X">X坐标</param>
      /// <param name="Y">Y坐标</param>
      /// <param name="mode">事件模式方式</param>
      /// <returns></returns>
        public static void LeftDoubleClick(IntPtr hWnd, int X, int Y, EventMode mode)
        {
            switch (mode)
            {
               //通过软件方式进行鼠标模拟
                case EventMode.SoftwareMode:
                {
                   SendMessage(hWnd,WM_LBUTTONDBLCLK, MK_LBUTTON, ConvertIParam(X,Y));
                   break;
                }
                //硬件模拟之后需要还原鼠标的坐标点           
                case EventMode.HardwareMode:
                {
                      System.Drawing.Point __sourcePoint;
                      GetCursorPos(out __sourcePoint);    //获取当前鼠标位置,并保存
                      System.Drawing.Rectangle __rectangle;
                      if(Window.GetWindowRect(hWnd, out __rectangle))
                      {
                          MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,     __sourcePoint.X, __sourcePoint.Y, 0, 0);
                          MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN, __sourcePoint.X, __sourcePoint.Y, 0, 0);
                          MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP,   __sourcePoint.X, __sourcePoint.Y, 0, 0);
                          MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN, __sourcePoint.X, __sourcePoint.Y, 0, 0);
                          MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP,   __sourcePoint.X, __sourcePoint.Y, 0, 0);
                          //还原鼠标坐标点
                          __sourcePoint = PointToPixel(__sourcePoint.X, __sourcePoint.Y);
                          MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, __sourcePoint.X, __sourcePoint.Y, 0, 0);
                      }
                   break;
                }
            }
        }

    /// <summary>
    /// 软件 模拟鼠标右键双击事件 
    /// </summary>
    /// <param name="hWnd">窗口句柄</param>
    /// <param name="X">X坐标</param>
    /// <param name="Y">Y坐标</param>
    /// <returns></returns>
        public static void RigthDoubleClick(IntPtr hWnd, int X, int Y)
        {
            RigthDoubleClick(hWnd, X, Y, EventMode.SoftwareMode);
        }

     /// <summary>
     ///  模拟鼠标右键双击事件
     /// </summary>
     /// <param name="hWnd">窗口句柄</param>
     /// <param name="X">X坐标</param>
     /// <param name="Y">Y坐标</param>
     /// <param name="mode">事件模式方式</param>
     /// <returns></returns>
        public static void RigthDoubleClick(IntPtr hWnd, int X, int Y, EventMode mode)
        {
            switch (mode)
            {
                //通过软件方式进行鼠标模拟
                case EventMode.SoftwareMode:
                {
                    Mouse.SendMessage(hWnd,WM_RBUTTONDBLCLK,MK_RBUTTON,ConvertIParam(X,Y));                    
                    break;
                }
                //硬件模拟之后需要还原鼠标的坐标点           
                case EventMode.HardwareMode:
                {
                    System.Drawing.Point __sourcePoint;
                    GetCursorPos(out __sourcePoint);    //获取当前鼠标位置,并保存
                    System.Drawing.Rectangle __rectangle;
                    if(Window.GetWindowRect(hWnd, out __rectangle))
                    { //移动鼠标坐标点
                       System.Drawing.Point __destPoint = PointToPixel(__rectangle.Left + X, __rectangle.Top + Y);
                       MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,      __destPoint.X, __destPoint.Y, 0, 0);
                       MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN, __destPoint.X, __destPoint.Y, 0, 0);
                       MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP,   __destPoint.X, __destPoint.Y, 0, 0);
                       MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN, __destPoint.X, __destPoint.Y, 0, 0);
                       MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTUP,   __destPoint.X, __destPoint.Y, 0, 0);
                       //还原鼠标坐标点
                       __sourcePoint = PointToPixel(__sourcePoint.X, __sourcePoint.Y);
                       MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, __sourcePoint.X, __sourcePoint.Y, 0, 0);
                     }
                    break;
                }
            }
        }

    /// <summary>
    ///  软件 模拟鼠标移动
    /// </summary>
    /// <param name="hWnd">窗口句柄</param>
    /// <param name="X">X坐标</param>
    /// <param name="Y">Y坐标</param>
    /// <param name="mode">事件模式方式</param>
    /// <returns></returns>
        public static void Move(IntPtr hWnd, int X, int Y)
        {
            Move(hWnd,X,Y, EventMode.SoftwareMode);
        }

     /// <summary>
     ///  软件 模拟鼠标移动
     /// </summary>
     /// <param name="hWnd">窗口句柄</param>
     /// <param name="X">X坐标</param>
     /// <param name="Y">Y坐标</param>
     /// <param name="mode">事件模式方式</param>
     /// <returns></returns>
        public static void Move(IntPtr hWnd, int X, int Y, EventMode mode)
        {
            switch (mode)
            {
               //通过软件方式进行鼠标模拟
               case EventMode.SoftwareMode:
               {
                  SetCursorPos(X,Y);
                  SendMessage(hWnd,WM_MOUSEMOVE,0x0,ConvertIParam(X,Y));
                  break;
               }
               //硬件模拟之后需要还原鼠标的坐标点           
               case EventMode.HardwareMode:
               {
                  System.Drawing.Rectangle __rectangle;
                  if (Window.GetWindowRect(hWnd, out __rectangle))
                  {
                    //移动鼠标坐标点
                     System.Drawing.Point __destPoint = PointToPixel(__rectangle.Left + X, __rectangle.Top + Y);
                     MouseEvent(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, __destPoint.X, __destPoint.Y, 0, 0);                           
                  }
                  break;
               }
            }
        }
    }
}
